﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

namespace Jerry.DroidBuilder.Common
{
    public static class Connector
    {
        private static readonly List<string> ALL_LIBS = new List<string>
        {
            "core.jar",
            "ext.jar",
            "framework.jar",
            "android.policy.jar",
            "services.jar",
            "com.google.android.maps.jar",
            "javax.obex.jar",
            "core-junit.jar",
            "bouncycastle.jar",
            "com.android.location.provider.jar",
            "framework.honeycomb.jar",
            "telephony-common.jar",
            "telephony-msim.jar",
            "IZTESecurity.jar",
            "qcnvitems.jar",
            "qcom.fmradio.jar",
            "dolby_ds.jar",
            "com.qualcomm.location.vzw_library.jar",
            "qcrilhook.jar",
            "mms-common.jar",
            "voip-common.jar"
        };

        public static void Baksmali(string fileName)
        {
            Baksmali(fileName, true);
        }

        public static void Baksmali(string fileName, bool deleteOutIfExists)
        {
            Baksmali(null, fileName, deleteOutIfExists);
        }

        public static void Baksmali(string frameworkDirectory, string fileName)
        {
            Baksmali(frameworkDirectory, fileName, true);
        }

        public static void Baksmali(string frameworkDirectory, string fileName, bool deleteOutIfExists)
        {
            var arguments = string.Empty;

            if (string.IsNullOrEmpty(frameworkDirectory))
            {
                arguments = string.Format(" -Xss50M -Xmx1024M -jar .\\tools\\baksmali.jar -x \"{0}\"", fileName);
            }
            else
            {
                var fileNames = Directory.GetFiles(frameworkDirectory, "*.jar");
                var libs = new List<string>(ALL_LIBS);
                var excludes = new List<string>();

                foreach (var item in fileNames)
                {
                    var name = Path.GetFileName(item);

                    if (libs.Contains(name))
                    {
                        excludes.Add(name);
                    }
                }

                for (int i = libs.Count - 1; i >= 0; i--)
                {
                    if (!excludes.Contains(libs[i]))
                    {
                        libs.RemoveAt(i);
                    }
                }

                arguments = string.Format(" -Xss50M -Xmx1024M -jar .\\tools\\baksmali.jar -c {2} -d \"{0}\" -x \"{1}\"", frameworkDirectory, fileName, string.Join(":", libs.ToArray()));
                //arguments = string.Format(" -Xss50M -Xmx512M -jar .\\tools\\baksmali.jar -c core.jar:ext.jar:framework.jar:android.policy.jar:services.jar:com.google.android.maps.jar:javax.obex.jar:core-junit.jar:bouncycastle.jar -d \"{0}\" -x \"{1}\"", frameworkDirectory, fileName);
            }

            if (deleteOutIfExists && Directory.Exists(".\\out"))
            {
                Directory.Delete(".\\out", true);
            }

            var result = ExecuteCommand("java", arguments);

            if (!string.IsNullOrEmpty(result) /*&& result.IndexOf("ERROR", StringComparison.OrdinalIgnoreCase) >= 0*/)
            {
                throw new Exception(result);
            }
        }

        public static void Smali()
        {
            var arguments = string.Format(" -Xss100M -Xmx1024M -jar .\\tools\\smali.jar out -o classes.dex");

            var result = ExecuteCommand("java", arguments);

            if (!string.IsNullOrEmpty(result) && result.IndexOf("ERROR", StringComparison.OrdinalIgnoreCase) >= 0)
            {
                throw new Exception(result);
            }
        }

        public static void SignApk(string fileName, string targetName)
        {
            var arguments = string.Format(" -jar .\\tools\\signapk.jar .\\tools\\sign.x509.pem .\\tools\\sign.pk8 \"{0}\" \"{1}\"", fileName, targetName);

            var result = ExecuteCommand("java", arguments);

            if (!string.IsNullOrEmpty(result) && result.IndexOf("ERROR", StringComparison.OrdinalIgnoreCase) >= 0)
            {
                throw new Exception(result);
            }
        }

        public static bool ZipAlign(string fileName)
        {
            var aligned = false;
            var alignedFile = fileName + ".aligned";
            var arguments = string.Format(" -f 4 \"{0}\" \"{1}\"", fileName, alignedFile);

            var result = ExecuteCommand(".\\tools\\zipalign", arguments);

            if (!string.IsNullOrEmpty(result))
            {
                if (File.Exists(alignedFile))
                {
                    aligned = true;

                    File.Delete(fileName);
                    File.Move(alignedFile, fileName);
                }
            }

            if (File.Exists(alignedFile))
            {
                File.Delete(alignedFile);
            }

            return aligned;
        }

        public static string ApktoolInstall(string fileName)
        {
            var arguments = string.Format(" -jar .\\tools\\apktool.jar if \"{0}\"", fileName);

            return ExecuteCommand("java", arguments);
        }

        public static string Apktool(string fileName, string outputDirectory)
        {
            var arguments = string.Format(" -jar .\\tools\\apktool.jar d \"{0}\" \"{1}\"", fileName, outputDirectory);

            return ExecuteCommand("java", arguments);
        }

        public static string ExecuteCommand(string command, string arguments)
        {
            Process p;
            return ExecuteCommand(command, arguments, false, out p);
        }

        public static string ExecuteCommand(string command, string arguments, bool async, out Process process)
        {
            var result = string.Empty;

            process = new Process
            {
                StartInfo = new ProcessStartInfo(command, arguments)
                {
                    CreateNoWindow = true,
                    UseShellExecute = false,
                    RedirectStandardError = true,
                    WorkingDirectory = Util.ApplicationDirectory,
                }
            };

            process.Start();

            if (!async)
            {

                do
                {
                    process.WaitForExit(5000);

                    using (var reader = process.StandardError)
                    {
                        result = reader.ReadToEnd();
                    }
                } while (!process.HasExited);
            }

            return result;
        }
    }
}
